const Self = @This();

const std = @import("std");
const TerSeg = @import("terseg.zig");

segs: std.ArrayList(TerSeg),

const u = TerSeg.u;
const i = TerSeg.i;

pub const ONE = TerSeg.ONE;
pub const BitAdr = TerSeg.BitAdr;
pub const BitLen = TerSeg.BitLen;

pub fn init(allocator: std.mem.Allocator) Self {
    return Self{
        .segs = std.ArrayList(TerSeg).init(allocator),
    };
}

pub fn deinit(self: *Self) void {
    self.segs.deinit();
}

pub fn add(self: *Self, seg: TerSeg) !void {
    try self.segs.append(seg);
}

pub fn concat(self: *Self, segs: []const TerSeg) !void {
    try self.segs.appendSlice(segs);
}

pub fn len(self: Self) usize {
    return self.segs.items.len;
}

pub fn isEmpty(self: Self) bool {
    return self.segs.items.len == 0;
}

pub fn print(self: Self) void {
    var dex: usize = 0;
    while (dex + 1 < self.segs.items.len) : (dex += 2) {
        Self.printPair(self.segs.items[dex], self.segs.items[dex + 1], "\n");
    }
    if (dex < self.segs.items.len) {
        self.segs.items[dex].print("\n");
    }
}

pub fn printPair(upp: TerSeg, low: TerSeg, postfix: ?[]const u8) void {
    for (0..BitLen) |dex| {
        const posBit: u(BitAdr) = @intCast(dex);
        const uppExt = (upp.ext & (ONE << posBit)) != 0;
        const uppPol = (upp.pol & (ONE << posBit)) != 0;
        const lowExt = (low.ext & (ONE << posBit)) != 0;
        const lowPol = (low.pol & (ONE << posBit)) != 0;

        const uppColor = if (!uppExt) "90" else if (uppPol) "32" else "31";
        const lowColor = if (!lowExt) "100" else if (lowPol) "42" else "41";

        std.debug.print("\x1b[{s};{s}m▀\x1b[0m", .{ uppColor, lowColor });
    }
    if (postfix) |p| {
        std.debug.print("{s}", .{p});
    }
}

pub fn sort(self: *Self) void {
    std.sort.insertion(TerSeg, self.segs.items, {}, comptime struct {
        fn lessThan(_: void, a: TerSeg, b: TerSeg) bool {
            if (a.ext != b.ext) return a.ext > b.ext;
            return a.pol > b.pol;
        }
    }.lessThan);
}

test "Tercol - printing" {
    const allocator = std.testing.allocator;
    var col = init(allocator);
    defer col.deinit();

    // Test empty colrix
    std.debug.print("empty\n", .{});
    col.print(); // Should print nothing
    std.debug.print("\n", .{});

    // Test single segment
    try col.add(TerSeg.init(0b1110, 0b1010)); // Positive bits at positions 1 and 3
    std.debug.print("single 1 3\n", .{});
    col.print(); // Should print one line with upp segment and empty low segment
    std.debug.print("\n", .{});

    // Test pair of segments
    try col.add(TerSeg.init(0b0101, 0b0101)); // Positive bits at positions 0 and 2
    std.debug.print("pair 1 3 0 2\n", .{});
    col.print(); // Should print one line with both segments
    std.debug.print("\n", .{});

    // Test three segments (odd number)
    try col.add(TerSeg.init(0b1111, 0b0000)); // All bits set, all negative
    col.print(); // Should print two lines: first with first two segments, second with third segment and empty segment
    std.debug.print("\n", .{});

    // // Test segments with different patterns
    // var col2 = Tercol.init(allocator, 8);
    // defer col2.deinit();

    // Add segments with alternating patterns
    try col.add(TerSeg.init(0b1111, 0b0101)); // All bits set, alternating negative/positive
    try col.add(TerSeg.init(0b1111, 0b1010)); // All bits set, alternating positive/negative
    col.print(); // Should print one line with contrasting patterns
    std.debug.print("\n", .{});

    // Test segments with unset bits
    var col3 = init(allocator, 8);
    defer col3.deinit();

    try col3.add(TerSeg.init(0b1010, 0b1010)); // Only even positions set, all positive
    try col3.add(TerSeg.init(0b0101, 0b0101)); // Only odd positions set, all positive
    col3.print(); // Should print one line with alternating set/unset bits
    std.debug.print("\n", .{});
}
